import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
from typing import List

class YOLODetectorApp:
    """YOLO 检测器主界面类"""
    def __init__(self, root: tk.Tk):
        self.root = root
        self.root.title("YOLO 目标检测器")
        self.root.geometry("1000x700")

        # --- 应用程序数据 ---
        self.recent_files: List[str] = []
        self.max_recent_files = 5
        self.current_status = "就绪"
        self.current_fps = "N/A"

        # --- 创建界面组件 ---
        self.create_menu()
        self.create_main_layout()

        # 初始化界面状态
        self.update_status(self.current_status)
        self.update_fps(self.current_fps)
        self.update_recent_menu() # 初始化最近文件菜单

    def create_menu(self):
        """创建菜单栏"""
        self.menubar = tk.Menu(self.root)
        self.root.config(menu=self.menubar)

        # 文件菜单 (File Menu)
        self.file_menu = tk.Menu(self.menubar, tearoff=0)
        self.file_menu.add_command(label="打开图片 (Open Image)", command=self.open_images, accelerator="Ctrl+O")
        self.file_menu.add_command(label="打开视频 (Open Video)", command=self.open_video, accelerator="Ctrl+V")
        self.file_menu.add_command(label="打开摄像头 (Open Camera)", command=self.open_camera, accelerator="Ctrl+C")
        self.file_menu.add_separator()
        # 最近文件子菜单
        self.recent_menu = tk.Menu(self.file_menu, tearoff=0)
        self.file_menu.add_cascade(label="最近文件 (Recent Files)", menu=self.recent_menu)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="保存检测结果 (Save Result)", command=self.save_result, accelerator="Ctrl+S")
        self.file_menu.add_command(label="批量处理 (Batch Process)", command=self.batch_process)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="退出 (Exit)", command=self.exit_app, accelerator="Ctrl+Q")
        self.menubar.add_cascade(label="文件 (File)", menu=self.file_menu)

        # 模型菜单 (Model Menu) - 根据文档预留
        self.model_menu = tk.Menu(self.menubar, tearoff=0)
        # 这里可以添加模型相关的选项，例如：
        # self.model_menu.add_command(label="加载模型", command=self.load_model)
        # self.model_menu.add_command(label="模型设置", command=self.model_settings)
        self.menubar.add_cascade(label="模型 (Model)", menu=self.model_menu)

        # 绑定快捷键 (需要在根窗口绑定)
        self.root.bind_all("<Control-o>", lambda e: self.open_images())
        self.root.bind_all("<Control-v>", lambda e: self.open_video())
        self.root.bind_all("<Control-c>", lambda e: self.open_camera())
        self.root.bind_all("<Control-s>", lambda e: self.save_result())
        self.root.bind_all("<Control-q>", lambda e: self.exit_app())

    def create_main_layout(self):
        """创建主界面布局"""
        # --- 主容器 ---
        main_paned_window = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
        main_paned_window.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # --- 左侧面板 (输入与控制区) ---
        self.left_frame = ttk.Frame(main_paned_window, relief=tk.RAISED, borderwidth=1)
        main_paned_window.add(self.left_frame, weight=1)

        # --- 顶部控制按钮区 ---
        self.top_control_frame = ttk.Frame(self.left_frame)
        self.top_control_frame.pack(fill=tk.X, padx=5, pady=5)
        tk.Button(self.top_control_frame, text="打开图片", command=self.open_images).pack(side=tk.LEFT, padx=2)
        tk.Button(self.top_control_frame, text="打开视频", command=self.open_video).pack(side=tk.LEFT, padx=2)
        tk.Button(self.top_control_frame, text="打开摄像头", command=self.open_camera).pack(side=tk.LEFT, padx=2)
        tk.Button(self.top_control_frame, text="保存结果", command=self.save_result).pack(side=tk.LEFT, padx=2)
        tk.Button(self.top_control_frame, text="批量处理", command=self.batch_process).pack(side=tk.LEFT, padx=2)

        # --- 输入源管理区 ---
        input_group = ttk.LabelFrame(self.left_frame, text="输入源管理", padding=(5, 5))
        input_group.pack(fill=tk.X, padx=5, pady=5)

        tk.Button(input_group, text="选择图片文件", command=self.open_images).pack(fill=tk.X, pady=2)
        tk.Button(input_group, text="选择视频文件", command=self.open_video).pack(fill=tk.X, pady=2)
        tk.Button(input_group, text="选择摄像头设备", command=self.open_camera).pack(fill=tk.X, pady=2)

        # --- 最近文件区 ---
        recent_group = ttk.LabelFrame(self.left_frame, text="最近文件", padding=(5, 5))
        recent_group.pack(fill=tk.X, padx=5, pady=5, anchor='nw')
        self.recent_files_frame = ttk.Frame(recent_group)
        self.recent_files_frame.pack(fill=tk.X)

        # --- 结果存储区 ---
        storage_group = ttk.LabelFrame(self.left_frame, text="结果存储", padding=(5, 5))
        storage_group.pack(fill=tk.X, padx=5, pady=5)
        tk.Button(storage_group, text="保存检测结果", command=self.save_result).pack(fill=tk.X, pady=2)
        tk.Button(storage_group, text="批量处理", command=self.batch_process).pack(fill=tk.X, pady=2)

        # --- 模型参数设置区 (示例) ---
        model_params_group = ttk.LabelFrame(self.left_frame, text="模型参数设置", padding=(5, 5))
        model_params_group.pack(fill=tk.X, padx=5, pady=5)

        # Confidence Threshold
        ttk.Label(model_params_group, text="置信度阈值:").grid(row=0, column=0, sticky=tk.W)
        self.confidence_var = tk.DoubleVar(value=0.5)
        conf_scale = ttk.Scale(model_params_group, from_=0.0, to=1.0, orient=tk.HORIZONTAL, variable=self.confidence_var)
        conf_scale.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5)
        self.confidence_label = ttk.Label(model_params_group, text=f"{self.confidence_var.get():.2f}")
        self.confidence_label.grid(row=0, column=2, padx=(5, 0))
        conf_scale.configure(command=lambda v: self.confidence_label.config(text=f"{float(v):.2f}"))
        model_params_group.columnconfigure(1, weight=1) # 让滑块可以拉伸

        # NMS Threshold
        ttk.Label(model_params_group, text="NMS阈值:").grid(row=1, column=0, sticky=tk.W, pady=(5, 0))
        self.nms_var = tk.DoubleVar(value=0.4)
        nms_scale = ttk.Scale(model_params_group, from_=0.0, to=1.0, orient=tk.HORIZONTAL, variable=self.nms_var)
        nms_scale.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=5, pady=(5, 0))
        self.nms_label = ttk.Label(model_params_group, text=f"{self.nms_var.get():.2f}")
        self.nms_label.grid(row=1, column=2, padx=(5, 0), pady=(5, 0))
        nms_scale.configure(command=lambda v: self.nms_label.config(text=f"{float(v):.2f}"))


        # --- 中间面板 (结果显示区) ---
        self.center_frame = ttk.Frame(main_paned_window, relief=tk.SUNKEN, borderwidth=1)
        main_paned_window.add(self.center_frame, weight=3) # 给中间区域更大权重

        # 使用 Text 控件模拟结果显示 (实际应为 Canvas 或 Label 用于图像)
        result_label = ttk.Label(self.center_frame, text="检测结果显示区", font=("Arial", 12))
        result_label.pack(pady=5)
        self.result_text = tk.Text(self.center_frame, wrap=tk.WORD, state=tk.DISABLED)
        scrollbar = ttk.Scrollbar(self.center_frame, orient=tk.VERTICAL, command=self.result_text.yview)
        self.result_text.configure(yscrollcommand=scrollbar.set)
        self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0), pady=5)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y, pady=5, padx=(0, 5))

        # --- 右侧面板 (状态与辅助区) ---
        self.right_frame = ttk.Frame(main_paned_window) # 右侧面板
        main_paned_window.add(self.right_frame, weight=1)

        status_group = ttk.LabelFrame(self.right_frame, text="状态", padding=(5, 5))
        status_group.pack(fill=tk.X, padx=5, pady=5, anchor='nw')

        self.status_label = ttk.Label(status_group, text="状态: 就绪")
        self.status_label.pack(anchor='w')
        self.fps_label = ttk.Label(status_group, text="FPS: N/A")
        self.fps_label.pack(anchor='w')
        self.error_label = ttk.Label(status_group, text="", foreground="red") # 错误提示
        self.error_label.pack(anchor='w')

        helper_group = ttk.LabelFrame(self.right_frame, text="辅助", padding=(5, 5))
        helper_group.pack(fill=tk.X, padx=5, pady=5, anchor='nw')

        tk.Button(helper_group, text="帮助", command=self.show_help).pack(fill=tk.X, pady=2)
        tk.Button(helper_group, text="关于", command=self.show_about).pack(fill=tk.X, pady=2)
        tk.Button(helper_group, text="退出", command=self.exit_app).pack(fill=tk.X, pady=2)

        # --- 底部状态栏 ---
        self.status_bar_var = tk.StringVar()
        self.status_bar = ttk.Label(self.root, textvariable=self.status_bar_var, relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)

    # --- 菜单和按钮对应的命令函数 ---

    def open_images(self):
        """打开图片文件"""
        file_paths = filedialog.askopenfilenames(
            title="选择图片文件",
            filetypes=[("图片文件", "*.jpg *.jpeg *.png *.bmp *.tiff"), ("所有文件", "*.*")]
        )
        if file_paths:
            self.log_message(f"已选择图片: {', '.join(os.path.basename(p) for p in file_paths[:3])}...")
            # 更新最近文件列表
            for path in file_paths:
                if path in self.recent_files:
                    self.recent_files.remove(path)
                self.recent_files.insert(0, path)
            self.recent_files = self.recent_files[:self.max_recent_files]
            self.update_recent_menu()
            self.update_status("已加载图片")

    def open_video(self):
        """打开视频文件"""
        file_path = filedialog.askopenfilename(
            title="选择视频文件",
            filetypes=[("视频文件", "*.mp4 *.avi *.mov *.mkv"), ("所有文件", "*.*")]
        )
        if file_path:
            self.log_message(f"已选择视频: {os.path.basename(file_path)}")
            # 更新最近文件列表
            if file_path in self.recent_files:
                self.recent_files.remove(file_path)
            self.recent_files.insert(0, file_path)
            self.recent_files = self.recent_files[:self.max_recent_files]
            self.update_recent_menu()
            self.update_status("已加载视频")

    def open_camera(self):
        """打开摄像头"""
        # 这里需要弹出设备选择对话框，实际实现需要 OpenCV
        # messagebox.showinfo("提示", "打开摄像头功能 (需选择设备)")
        self.log_message("尝试打开摄像头...")
        self.update_status("摄像头已打开")

    def save_result(self):
        """保存检测结果"""
        # 需要先有检测结果才能保存
        save_path = filedialog.asksaveasfilename(
            title="保存检测结果",
            defaultextension=".jpg",
            filetypes=[("JPEG", "*.jpg"), ("PNG", "*.png"), ("所有文件", "*.*")]
        )
        if save_path:
            self.log_message(f"检测结果已保存至: {save_path}")
            self.update_status("结果已保存")

    def batch_process(self):
        """批量处理"""
        folder_path = filedialog.askdirectory(title="选择包含图片/视频的文件夹")
        if folder_path:
            self.log_message(f"开始批量处理文件夹: {folder_path}")
            self.update_status("批量处理中...")

    def exit_app(self):
        """退出应用程序"""
        if messagebox.askokcancel("退出", "确定要退出程序吗?"):
            self.root.destroy()

    def show_help(self):
        """显示帮助信息"""
        help_text = (
            "YOLO 目标检测器 - 使用说明\n\n"
            "1. 文件 -> 打开图片/视频/摄像头 或使用顶部按钮加载输入源。\n"
            "2. 在左侧设置模型参数（如置信度、NMS阈值）。\n"
            "3. (执行检测操作 - 此功能需后端实现)。\n"
            "4. 文件 -> 保存检测结果 可保存当前检测图像。\n"
            "5. 文件 -> 批量处理 可处理整个文件夹。\n"
            "6. 使用 文件 -> 退出 或右侧面板的退出按钮关闭程序。"
        )
        messagebox.showinfo("帮助", help_text)

    def show_about(self):
        """显示关于信息"""
        about_text = (
            "软件名称: YOLO 目标检测器\n"
            "版本: 1.0.0\n"
            "YOLO版本: v5/v8 (根据实际模型)\n"
            "描述: 一个基于 YOLO 算法的图形界面目标检测工具。"
        )
        messagebox.showinfo("关于", about_text)

    # --- 辅助函数 ---

    def log_message(self, message: str):
        """在结果显示区添加日志信息"""
        self.result_text.config(state=tk.NORMAL)
        self.result_text.insert(tk.END, message + "\n")
        self.result_text.see(tk.END) # 滚动到底部
        self.result_text.config(state=tk.DISABLED)

    def update_status(self, status: str):
        """更新状态标签和状态栏"""
        self.current_status = status
        self.status_label.config(text=f"状态: {status}")
        self.status_bar_var.set(f"{status} | FPS: {self.current_fps}")

    def update_fps(self, fps: str):
        """更新FPS显示"""
        self.current_fps = fps
        self.fps_label.config(text=f"FPS: {fps}")
        # 同时更新状态栏的FPS
        self.status_bar_var.set(f"{self.current_status} | FPS: {fps}")

    def update_recent_menu(self):
        """更新最近文件菜单"""
        self.recent_menu.delete(0, tk.END)
        if not self.recent_files:
            self.recent_menu.add_command(label="无最近文件", state=tk.DISABLED)
        else:
            for i, file_path in enumerate(self.recent_files):
                filename = os.path.basename(file_path)
                # 使用 lambda 的默认参数技巧来捕获当前 file_path 的值
                self.recent_menu.add_command(
                    label=f"{i+1}. {filename}",
                    command=lambda path=file_path: self.open_recent_file(path)
                )
        self.recent_menu.add_separator()
        self.recent_menu.add_command(label="清空历史记录", command=self.clear_recent_files)

    def open_recent_file(self, file_path: str):
        """打开最近文件"""
        if os.path.exists(file_path):
            _, ext = os.path.splitext(file_path.lower())
            if ext in ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']:
                self.log_message(f"从历史记录打开图片: {os.path.basename(file_path)}")
                self.update_status("已加载图片")
            elif ext in ['.mp4', '.avi', '.mov', '.mkv']:
                self.log_message(f"从历史记录打开视频: {os.path.basename(file_path)}")
                self.update_status("已加载视频")
            # 可以添加对其他类型文件的处理
        else:
            messagebox.showerror("错误", f"文件不存在: {file_path}")
            if file_path in self.recent_files:
                self.recent_files.remove(file_path)
            self.update_recent_menu()

    def clear_recent_files(self):
        """清空最近文件列表"""
        self.recent_files.clear()
        self.update_recent_menu()

# --- 主程序入口 ---
if __name__ == "__main__":
    root = tk.Tk()
    app = YOLODetectorApp(root)
    # 模拟 FPS 更新 (可选)
    def simulate_fps():
        import random
        app.update_fps(f"{random.randint(20, 60)}")
        root.after(1000, simulate_fps) # 每秒更新一次
    # root.after(1000, simulate_fps) # 取消注释以启用模拟FPS

    root.mainloop()